package cn.cj.erp.common.excel.excel;

import cn.cj.erp.common.excel.anno.Excel;
import cn.cj.erp.common.excel.anno.Excels;
import cn.cj.erp.common.excel.enums.ExcelType;
import cn.cj.erp.common.exception.ServiceException;
import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.reflect.FieldUtils;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;

import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.util.*;
import java.util.stream.Collectors;

/**
 * Excel处理对象初始化器
 *
 * @author jianchen
 */
@Slf4j
public class ExcelBeanInitializer<T> {
    private final ExcelBean<T> excelBean;

    /**
     * 获取Excel初始化器，非单例
     * @param excelBean Excel对象
     * @return Excel初始化器
     */
    public static <T> ExcelBeanInitializer<T> getInstance(ExcelBean<T> excelBean) {
        return new ExcelBeanInitializer<>(excelBean);
    }

    private ExcelBeanInitializer(ExcelBean<T> excelBean) {
        this.excelBean = excelBean;
    }

    public void importInit(InputStream is, String sheetName) {
        excelBean.setType(ExcelType.IMPORT);
        try {
            excelBean.setWb(WorkbookFactory.create(is));
            Sheet sheet;
            if (StrUtil.isNotEmpty(sheetName)) {
                sheet = excelBean.getWb().getSheet(sheetName);
            } else {
                sheet = excelBean.getWb().getSheetAt(0);
            }
            excelBean.setSheet(sheet);
            if (sheet == null) {
                throw new IOException("文件sheet不存在");
            }
        } catch (Exception e) {
            log.error("导入初始化失败", e);
            throw new ServiceException("导入初始化失败", e);
        }
        createExcelField(excelBean);
    }

    public void exportInit(List<T> list, String sheetName, String title) {
        this.init(list, sheetName, title, ExcelType.EXPORT);
    }

    /**
     * 初始化Excel内部对象
     */
    private void init(List<T> list, String sheetName, String title, ExcelType type) {
        if (list == null) {
            list = new ArrayList<>();
        }
        excelBean.setList(list);
        excelBean.setSheetName(sheetName);
        excelBean.setType(type);
        excelBean.setTitle(title);

        createExcelField(excelBean);
        createWorkbook(excelBean);
        excelBean.createTitle();
        createSubHead(excelBean);
    }

    /**
     * 得到所有定义字段
     */
    private void createExcelField(ExcelBean<T> excelBean) {
        List<Object[]> fields = getAnnoFields(excelBean);
        // 对字段进行排序
        fields = fields.stream()
                .sorted(Comparator.comparing(objects -> ((Excel) objects[1]).sort()))
                .collect(Collectors.toList());
        excelBean.setFields(fields);
        excelBean.setMaxHeight(this.getRowHeight(fields));
    }

    /**
     * 创建一个工作簿
     */
    private void createWorkbook(ExcelBean<T> excelBean) {
        excelBean.setWb(new SXSSFWorkbook(500));
        excelBean.setSheet(excelBean.getWb().createSheet());
        excelBean.getWb().setSheetName(0, excelBean.getSheetName());
        excelBean.setStyles(createStyles(excelBean));
    }

    /**
     * 创建对象的子列表名称
     */
    private void createSubHead(ExcelBean<T> excelBean) {
        if (excelBean.hasSubList()) {
            excelBean.setSubMergedFirstRowNum(excelBean.getSubMergedFirstRowNum() + 1);
            excelBean.setSubMergedLastRowNum(excelBean.getSubMergedLastRowNum() + 1);
            Row subRow = excelBean.getSheet().createRow(excelBean.getRowNum());
            int excelNum = 0;
            for (Object[] objects : excelBean.getFields()) {
                Excel attr = (Excel) objects[1];
                Cell headCell1 = subRow.createCell(excelNum);
                headCell1.setCellValue(attr.name());
                headCell1.setCellStyle(excelBean.getCellStyle(ExcelFieldHandler.getHeadStyleKey(attr)));
                excelNum++;
            }
            int headFirstRow = excelNum - 1;
            int headLastRow = headFirstRow + excelBean.getSubFields().size() - 1;
            if (headLastRow > headFirstRow) {
                CellRangeAddress rangeAddress =
                        new CellRangeAddress(excelBean.getRowNum(), excelBean.getRowNum(), headFirstRow, headLastRow);
                excelBean.getSheet().addMergedRegion(rangeAddress);
            }
            excelBean.setRowNum(excelBean.getRowNum() + 1);
        }
    }

    /**
     * 获取字段注解信息
     */
    private List<Object[]> getAnnoFields(ExcelBean<T> excelBean) {
        List<Object[]> fields = new ArrayList<>();
        List<Field> tempFields = new ArrayList<>();
        tempFields.addAll(Arrays.asList(excelBean.getClazz().getSuperclass().getDeclaredFields()));
        tempFields.addAll(Arrays.asList(excelBean.getClazz().getDeclaredFields()));
        for (Field field : tempFields) {
            if (!ArrayUtils.contains(excelBean.getExcludeFields(), field.getName())) {
                //默认错误信息列不导出
                if ("importErrInfo".equals(field.getName())) {
                    continue;
                }
                // 单注解
                if (field.isAnnotationPresent(Excel.class)) {
                    Excel attr = field.getAnnotation(Excel.class);
                    if (attr != null && (attr.type() == ExcelType.ALL || attr.type() == excelBean.getType())) {
                        field.setAccessible(true);
                        fields.add(new Object[]{field, attr});
                    }
                    if (Collection.class.isAssignableFrom(field.getType())) {
                        excelBean.setSubMethod(ExcelFieldHandler.getSubMethod(field.getName(), excelBean.getClazz()));
                        ParameterizedType pt = (ParameterizedType) field.getGenericType();
                        Class<?> subClass = (Class<?>) pt.getActualTypeArguments()[0];
                        excelBean.setSubFields(FieldUtils.getFieldsListWithAnnotation(subClass, Excel.class));
                    }
                }

                // 多注解
                if (field.isAnnotationPresent(Excels.class)) {
                    Excels attrs = field.getAnnotation(Excels.class);
                    Excel[] excels = attrs.value();
                    for (Excel attr : excels) {
                        if (attr != null && (attr.type() == ExcelType.ALL || attr.type() == excelBean.getType())) {
                            field.setAccessible(true);
                            fields.add(new Object[]{field, attr});
                        }
                    }
                }
            }
        }
        return fields;
    }

    /**
     * 根据注解获取最大行高
     */
    private short getRowHeight(List<Object[]> fields) {
        double maxHeight = 0;
        for (Object[] os : fields) {
            Excel excel = (Excel) os[1];
            maxHeight = Math.max(maxHeight, excel.height());
        }
        return (short) (maxHeight * 20);
    }

    /**
     * 创建表格样式
     *
     * @param excelBean ExcelBo
     * @return 样式列表
     */
    private Map<String, CellStyle> createStyles(ExcelBean<T> excelBean) {
        Workbook wb = excelBean.getWb();
        // 写入各条记录,每条记录对应excel表中的一行
        Map<String, CellStyle> styles = new HashMap<>();
        CellStyle style = wb.createCellStyle();
        style.setAlignment(HorizontalAlignment.CENTER);
        style.setVerticalAlignment(VerticalAlignment.CENTER);
        Font titleFont = wb.createFont();
        titleFont.setFontName("Arial");
        titleFont.setFontHeightInPoints((short) 16);
        titleFont.setBold(true);
        style.setFont(titleFont);
        styles.put("title", style);

        style = wb.createCellStyle();
        style.setAlignment(HorizontalAlignment.CENTER);
        style.setVerticalAlignment(VerticalAlignment.CENTER);
        setCellBorder(style);
        Font dataFont = wb.createFont();
        dataFont.setFontName("Arial");
        dataFont.setFontHeightInPoints((short) 10);
        style.setFont(dataFont);
        styles.put("data", style);

        style = wb.createCellStyle();
        style.setAlignment(HorizontalAlignment.CENTER);
        style.setVerticalAlignment(VerticalAlignment.CENTER);
        Font totalFont = wb.createFont();
        totalFont.setFontName("Arial");
        totalFont.setFontHeightInPoints((short) 10);
        style.setFont(totalFont);
        styles.put("total", style);

        styles.putAll(createAnnoHeaderStyles(excelBean, wb, styles));
        styles.putAll(creatDataAnnoStyles(excelBean, wb));
        return styles;
    }

    /**
     * 根据Excel注解创建表格头样式
     *
     * @param wb 工作薄对象
     * @return 自定义样式列表
     */
    private Map<String, CellStyle> createAnnoHeaderStyles(ExcelBean<T> excelBean, Workbook wb, Map<String, CellStyle> styles) {
        Map<String, CellStyle> headerStyles = new HashMap<>();
        for (Object[] os : excelBean.getFields()) {
            Excel excel = (Excel) os[1];
            String key = ExcelFieldHandler.getHeadStyleKey(excel);
            if (!headerStyles.containsKey(key)) {
                CellStyle style = wb.createCellStyle();
                style.cloneStyleFrom(styles.get("data"));
                style.setAlignment(HorizontalAlignment.CENTER);
                style.setVerticalAlignment(VerticalAlignment.CENTER);
                style.setFillForegroundColor(excel.headerBackgroundColor().index);
                style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
                Font headerFont = wb.createFont();
                headerFont.setFontName("Arial");
                headerFont.setFontHeightInPoints((short) 10);
                headerFont.setBold(true);
                headerFont.setColor(excel.headerColor().index);
                style.setFont(headerFont);
                headerStyles.put(key, style);
            }
        }
        return headerStyles;
    }

    /**
     * 根据Excel注解创建表格列样式
     *
     * @param wb 工作薄对象
     * @return 自定义样式列表
     */
    private Map<String, CellStyle> creatDataAnnoStyles(ExcelBean<T> excelBean, Workbook wb) {
        Map<String, CellStyle> styles = new HashMap<>();
        for (Object[] os : excelBean.getFields()) {
            Excel excel = (Excel) os[1];
            String key = ExcelFieldHandler.getDataStyleKey(excel);
            if (!styles.containsKey(key)) {
                CellStyle style = wb.createCellStyle();
                style.setAlignment(excel.align());
                style.setVerticalAlignment(VerticalAlignment.CENTER);
                setCellBorder(style);
                style.setFillPattern(FillPatternType.SOLID_FOREGROUND);
                style.setFillForegroundColor(excel.backgroundColor().getIndex());
                Font dataFont = wb.createFont();
                dataFont.setFontName("Arial");
                dataFont.setFontHeightInPoints((short) 10);
                dataFont.setColor(excel.color().index);
                style.setFont(dataFont);
                styles.put(key, style);
            }
        }
        return styles;
    }

    /**
     * 设置单元格边框
     * @param style 单元格样式
     */
    private void setCellBorder(CellStyle style) {
        style.setBorderRight(BorderStyle.THIN);
        style.setRightBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
        style.setBorderLeft(BorderStyle.THIN);
        style.setLeftBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
        style.setBorderTop(BorderStyle.THIN);
        style.setTopBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
        style.setBorderBottom(BorderStyle.THIN);
        style.setBottomBorderColor(IndexedColors.GREY_50_PERCENT.getIndex());
    }
}